engine: key removeNic work job lookup on nic uuid to fix concurrent removal race#13700
engine: key removeNic work job lookup on nic uuid to fix concurrent removal race#13700Andr0human wants to merge 1 commit into
Conversation
|
Congratulations on your first Pull Request and welcome to the Apache CloudStack community! If you have any issues or are unsure about any anything please check our Contribution Guide (https://github.com/apache/cloudstack/blob/main/CONTRIBUTING.md)
|
…emoval race removeNicFromVmThroughJobQueue looked up pending work jobs by (vmType, vmId, commandName) only, so the nic was not part of the dedup key. A second removeNicFromVirtualMachine request for a different nic on the same vm matched the first still-pending VmWorkRemoveNicFromVm job and joined it instead of submitting its own. That job removes only the nic it was created for, yet both callers wait on the same job id and both receive its success, leaving the second nic silently attached while its API call reports success. Make the nic uuid part of the lookup key, mirroring addVmToNetworkThroughJobQueue which was fixed the same way in apache#5658: - look up pending jobs with the 4-arg listPendingWorkJobs(Instance, vmId, cmd, nic.getUuid()) - fail fast with CloudRuntimeException if more than one job matches - stamp new jobs with setSecondaryObjectIdentifier(nic.getUuid()) before submitting Genuine duplicates, two requests for the same nic, still dedup as before. Adds three regression tests to VirtualMachineManagerImplTest covering the cross-nic race, same-nic dedup, and the multiple-pending-jobs guard. Fixes: apache#13699 Generated-by: Claude Code (Anthropic) Signed-off-by: Ayush Sinha <ayushsinha3199@gmail.com>
a8054a6 to
09bed9d
Compare
There was a problem hiding this comment.
Pull request overview
This PR fixes a concurrency/deduplication race in VirtualMachineManagerImpl.removeNicFromVmThroughJobQueue by including the NIC UUID in the pending work-job lookup key, preventing concurrent remove-NIC requests for different NICs on the same VM from collapsing into a single work job.
Changes:
- Key pending
VmWorkRemoveNicFromVmjob lookup by(vmId, commandName, nicUuid)instead of NIC-agnostic(vmId, commandName). - Stamp newly created remove-NIC work jobs with
secondary_object= NIC UUID. - Add unit tests covering per-NIC dedup, non-joining across different NICs, and the multiple-pending-jobs guard.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java | Uses NIC UUID as the dedup key for pending remove-NIC work jobs and stamps new jobs with the NIC UUID. |
| engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java | Adds regression tests ensuring correct per-NIC work job behavior under concurrency and inconsistent pending-job states. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| VmWorkJobVO workJob; | ||
| if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { | ||
| if (pendingWorkJobs.size() > 1) { | ||
| throw new CloudRuntimeException(String.format("The number of jobs to remove nic %s from vm %s are %d", nic.getUuid(), vm.getInstanceName(), pendingWorkJobs.size())); |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## 4.22 #13700 +/- ##
=========================================
Coverage 17.69% 17.69%
Complexity 15833 15833
=========================================
Files 5925 5925
Lines 533534 533539 +5
Branches 65273 65274 +1
=========================================
+ Hits 94421 94429 +8
+ Misses 428434 428431 -3
Partials 10679 10679
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Description
This PR makes the NIC uuid part of the pending work-job lookup key in
VirtualMachineManagerImpl.removeNicFromVmThroughJobQueue, so that two concurrentremoveNicFromVirtualMachinerequests for different NICs on the same VM no longer collapse into a single work job.Before this change the lookup was NIC-agnostic —
retrievePendingWorkJob(vmId, commandName), i.e. the 3-argVmWorkJobDao.listPendingWorkJobs(type, vmId, cmd). If a second remove-NIC request arrived while the firstVmWorkRemoveNicFromVmjob was still pending, it matched that job and joined it instead of submitting its own. The job removes only the NIC it was created for, but both callers wait on the same job id and both receive its success — so the second NIC stays attached while its API call reports success.Functional change in behaviour:
This is the mirror of an already-merged fix on the add path.
addVmToNetworkThroughJobQueuewas fixed in #5658 (issue #5541, 4.16.1.0) by keying on the object uuid; the symmetric remove path never got the same treatment. The change here follows that established pattern line for line:listPendingWorkJobs(Instance, vmId, VmWorkRemoveNicFromVm.class.getName(), nic.getUuid());CloudRuntimeException(same guard as the add path);workJob.setSecondaryObjectIdentifier(nic.getUuid())before submitting.Nic extends Identity, sogetUuid()is available — no interface or schema change is required.Scope is deliberately tight: only
removeNicFromVmThroughJobQueueis touched. The siblingremoveVmFromNetworkThroughJobQueuestill uses the network-agnosticretrievePendingWorkJob(vmId, commandName)lookup and appears to have the same class of race; that is left out of this PR and noted in the issue as a follow-up.Fixes: #13699
Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
N/A — management-server logic change, no UI surface.
How Has This Been Tested?
1. Unit tests — three regression tests added to
VirtualMachineManagerImplTestremoveNicFromVmThroughJobQueueDoesNotJoinAnotherNicsPendingJobremoveNicFromVmThroughJobQueueJoinsExistingJobForSameNicremoveNicFromVmThroughJobQueueThrowsWhenMultiplePendingJobsForSameNicsize() > 1guard throwsRed→green was confirmed by reverting the production change and re-running against this branch:
Tests run: 96, Failures: 1, Errors: 2— all three new tests and only those, with the regression test failing at thesetCmdInfoAndSubmitAsyncJobverification, i.e. precisely because the second request joined the first NIC's job instead of submitting its own.checkstyle:checkon the module reports 0 violations.2. End-to-end on the CloudStack simulator
Advanced-zone simulator (
mvn -Pdeveloper -Dsimulator,tools/docker), VM with three NICs, two of them removed concurrently.UnPlugNicCommandwas stalled withconfigureSimulator name=UnPlugNicCommand value=wait:20000so the first work job is guaranteed to still be pending when the second request arrives — that is the window the bug lives in. Both halves were built through the same two-module pipeline (-pl engine/orchestrationthen-pl client), so the only difference between the runs is the fix itself. (This end-to-end run was done on a simulator build ofmain; the diff proposed here is identical on both branches, and the unit tests above were run on this 4.22 branch.)vm_work_jobrows forVmWorkRemoveNicFromVmsecondary_object(the per-NIC key)NULLThe unfixed run reports the contradiction directly — the caller is told the removal succeeded while
listVirtualMachinesstill shows the NIC attached:and the same query after the fix shows the two per-NIC jobs the dedup key now produces:
How did you try to break this feature and the system with this change?
removeNicFromVmThroughJobQueueJoinsExistingJobForSameNic.removeNicFromVmThroughJobQueueThrowsWhenMultiplePendingJobsForSameNic.secondary_objectis an existingvm_work_jobcolumn already written by the add path since remove VmWorkJob after adding a nic to a vm #5658 — no schema change, and it is confirmed populated in the simulator run above.setSecondaryObjectIdentifieris only ever set here for jobs whosecmdisVmWorkRemoveNicFromVm, and the 4-arglistPendingWorkJobsoverload already exists and is already used byaddVmToNetworkThroughJobQueue. No other work-job path changes behaviour; the fullVirtualMachineManagerImplTestsuite (96 tests) passes.